/*
 * Alex Rodrigo de Oliveira - 06/30403
 * Rafaela Porto Osório - 06/94011
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/msg.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include "list.h"
#include "mqueue.h"
#include "msg_handlers.h"
#include "sig_handlers.h"
#include "debug.h"


/*
 * Funções auxiliares da main.
 */
static void receive_and_exec();
static void housekeep();


/*
 * Main.
 * Essa função nunca deve retornar, pois o deamon fica executando em loop
 * infinito até que recebe um SIGINT, SIGTERM ou uma mensagem do tipo MSG_TERM.
 */
int main(int argc, char **argv)
{
	atexit(housekeep);				// registra a função de arrumação da casa
	init_msg_handlers();			// registra as funções de tratamento de mensagens
	init_sig_handlers();			// registra as funções de tratamento dos sinais
	mqin  = mqueue_create_in();		// cria a fila de mensagens de recebimento
	mqout = mqueue_create_out();	// cria a fila de mensagens de respostas
	list_new(&schedule);			// inicializa lista de processos agendados
	list_new(&running);				// inicializa lista de processos em execução
	receive_and_exec();				// lê mensagens da fila e agenda a execução

	return 1;		// a execução jamais deve chegar aqui
}


/*
 * Main event loop.
 * Fica em loop infinito recebendo mensagens da fila, e chamando a função
 * adequada para o tratamento de cada tipo de mensagem.
 */
static void receive_and_exec()
{
	int ret;
	Msg msg;
	
	for (;;) {

		/* Tenta receber uma mensagem da fila. O processo vai ficar bloqueado
			até que uma das três coisas a seguir aconteçam:
			(1) Se ele conseguir obter a mensagem, o código continua.
			(2) Se chegar um sinal, trata o sinal e volta para o início do loop.
			(3) Se for um erro desconhecido, aborta o programa. */
		ret = msgrcv(mqin, &msg, sizeof(Job), 0, 0);
		if (ret == -1) {
			if (errno == EINTR) continue;
			perror("Não foi possível obter mensagem da fila");
			exit(1);
		}

		assert(msg.type != 0);
		debug_print("Mensagem recebida. Tipo da mensagem = %ld\n", msg.type);
		
		/* Chama a função correta de acordo com o tipo da mensagem. */
		handlers[msg.type](&msg);
	}
}


/*
 * Deleta as filas de mensagens e libera a memória das listas. Essa função é
 * executada sempre que a função exit é chamada ou quando recebemos uma mensagem
 * de término. Ela só não vai ser executada se o programa for abortado (devido a
 * um SIGSGEGV, por exemplo), nesse caso as filas de mensagens não serão
 * deletadas.
 */
static void housekeep()
{
	struct msqid_ds buffer;

	msgctl(mqin, IPC_RMID, &buffer);
	msgctl(mqout, IPC_RMID, &buffer);
	list_del(&schedule);
	list_del(&running);
}
